use std::cell::Ref;

/// trait对象的 生命周期
/// trait对象和生命周期有默认遵循的规则:
/// trait 对象的生命周期默认是 ’static。
/// · 如果实现trait的类型包含&’a x 或&'a mut x，则默认生命周期就是’a。
/// · 如果实现 trait 的类型只有 T : ’a，则默认生命周期就是’a。
/// · 如果实现 trait 的类型包含多个类似 T : ’a 的从 句， 则 生命周期需要明确指定
///
/// 如果一个trait对象作为一个类型参数传递到泛型中，那么它的生命约束会从它包含的类型中推断
/// · 如果包含的类型中有唯一的约束，那么就使用这个约束。
/// · 如果包含的类型中有超过一个约束，那么必须显式指定约束。
/// 如果以上都不适用，那么：
/// · 如果trait是以单个生命周期约束定义的，那么就使用这个约束
/// · 如果所有生命周期约束都是 'static 的，那么就使用 'static 作为约束
/// · 如果trait没有生命周期约束，那么它的生命周期将会从表达式中推断，如果不在表达式中，那么就是 'static 的
pub fn start() {
    let num = 5;
    let box_bar = Box::new(Bar { x: &num });
    let obj = box_bar as Box<dyn Foo1>;
}

trait Foo1 {}

#[derive(Debug)]
struct Bar<'a> {
    x: &'a i32,
}

impl<'a> Foo1 for Bar<'a> {}

fn foo_fn<'a>(x: &'a i32) -> Box<dyn Foo1 + 'a> {
    Box::new(Bar { x })
}

/// 如果实现 trait 的类型包含多个类似 T : ’a 的从句， 则生命周期需要明确指定
trait Foo2<'a> {}

struct FooImpl<'a> {
    s: &'a [u32],
}

impl<'a> Foo2<'a> for FooImpl<'a> {}

// 无法编译通过 `s` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement
// fn foo_fn<'a>(s: &'a [u32]) -> Box<dyn Foo2<'a>> {
//     Box::new(FooImpl {s})
// }

/// Box<Foo<’a>>是一个 trait对象，它的默认生命周期是’static 的。而现在实现trait Foo的类型 FooImpl
/// 有一个&’a [u32] 类型的成员，所以此时的 trait 对象生命周期应该是’a。 因此，如果想修复上面的错误 ，
/// 只需要显式地为 trait对象增加生命周期参数，将 Box<Foo<'a>>改为 Box<Foo<’a> + ’a>即可，
/// 此 时该 trait对象的生命周期就是'a 覆盖了默认的’static生命周期
fn foo2_fn<'a>(s: &'a [u32]) -> Box<dyn Foo2<'a> + 'a> {
    Box::new(FooImpl { s })
}

trait Trait {}

// 省略
type T1 = Box<dyn Trait>;

// 展开，Box<T>对T没有生命周期约束，所以被推断为'static
type T2 = Box<dyn Trait + 'static>;

// 省略
impl dyn Trait {}

// 展开
impl dyn Trait + 'static {}

// 省略
type T3<'a> = &'a dyn Trait;

// 展开, 因为&'a T 要求 T: 'a, 所以推断为 'a
type T4<'a> = &'a (dyn Trait + 'a);

// 省略
type T5<'a> = Ref<'a, dyn Trait>;

// 展开, 因为Ref<'a, T> 要求 T: 'a, 所以推断为 'a
type T6<'a> = Ref<'a, dyn Trait + 'a>;

trait GenericTrait<'a>: 'a {}

// 省略
type T7<'a> = Box<dyn GenericTrait<'a>>;

// 展开
type T8<'a> = Box<dyn GenericTrait<'a> + 'a>;

// 省略
impl<'a> dyn GenericTrait<'a> {}

// 展开
impl<'a> dyn GenericTrait<'a> + 'a {}

// 实现了某个trait的具体的类型可以包含引用，因此它们同样拥有生命周期约束，
// 且对应的trait对象也有生命周期约束。你也可以直接为引用实现trait，而引用显然有生命周期约束。
struct Struct {}
impl Trait for Struct {}
impl Trait for &Struct {} // 直接在引用类型上实现Trait
impl<'a, T> Trait for Ref<'a, T> {} // 在包含引用的类型上实现Trait
